ciftiToolsciftiTools is an R package for working with CIFTI-2 format brain imaging data. It supports the “dscalar,” “dtseries,” and “dlabel” CIFTI intents. It also supports GIFTI files for functional data (“metric” or “label”) and surface geometry data (“surf”). Reading, writing, resampling, and other operations on these files are made possible using the Connectome Workbench. Therefore, the Workbench must be installed to use ciftiTools. Visualizing these files is made possible using the rgl R package and integrated support of the surface GIFTI files.
To get started, we load the ciftiTools package and indicate where to find the Connectome Workbench folder:
library(ciftiTools)
# Replace '../../workbench' with the actual path to
# the Connectome Workbench folder on your computer.
ciftiTools.setOption('wb_path', '../../workbench')## Using this Workbench path: '../../workbench/bin_windows64/wb_command.exe'.
In this vignette, we will use example data included in the ciftiTools package. The files are originally from NITRC:
ciftiTools, the dscalar and dlabel CIFTIs were resampled to 6k and the “ones” dscalar was resampled to 1k.cifti_fnames <- ciftiTools::demo_files()$cifti
surfL_fname <- ciftiTools::demo_files()$surf["left"]
surfR_fname <- ciftiTools::demo_files()$surf["right"]The last preliminary step is to set up the R Markdown document for including ciftiTools graphics made with rgl. See the help page for view_xifti_surface for more information about embedding interactive widgets or static plots from ciftiTools into html documents.
library(rgl)
rgl::setupKnitr()
# Sometimes the first RGL window does not render properly.
rgl::rgl.open(); rgl::rgl.close()CIFTI files organize the gray matter of the brain into “greyordinates”: vertices representing the left and right cortical surfaces, and voxels representing the subcortical gray matter structures and the cerebellum. A CIFTI file consists of two parts: (1) a NIFTI XML header which contains all the metadata including medial wall locations, subcortical structure labels, and the subcortical volumetric mask; and (2) a matrix representing all the greyordinate data. These components are read in together with read_cifti:
cii <- read_cifti(cifti_fnames["dscalar"])
#cii <- read_cifti(cifti_fnames["dtseries"])
#cii <- read_cifti(cifti_fnames["dlabel"])
cii## Brain Structures: left cortex, right cortex
## left cortex: 5412 surface vertices, 2 measurements.
## right cortex: 5434 surface vertices, 2 measurements.
By default, read_cifti only reads in the left and right cortex data. The subcortical data can be included by using the argument brainstructures="all". Other brainstructure combinations can be specified too, e.g. brainstructures=c("left", "subcortical"). The full set of choices for brainstructures is any combination of "left", "right" and "subcortical", or "all" for all three.
The resulting object produced by read_cifti is a "xifti" with components data (the greyordinate data matrix, separated by brainstructure), meta (metadata, most of which is from the NIFTI XML header), and surf (surface geometry). The last component distinguishes a "xifti" from a CIFTI: the left and right cortical surface geometries are not included in CIFTI files, so they must be read from separate surface GIFTI files (ending in surf.gii). The surface must be compatible: the number of vertices must be the same, and each vertex in the CIFTI data must correspond to the vertex location in the corresponding GIFTI surface file. In this way, a "xifti" represents a combination of a CIFTI file with compatible GIFTI files for the cortical mesh.
We can add surfaces like so:
cii <- read_cifti(cifti_fnames["dtseries"])
cii <- add_surf(cii, surfL=surfL_fname, surfR=surfR_fname)
cii## Brain Structures: left cortex, right cortex, left surface, right surface
## left cortex: 30424 surface vertices, 2 measurements.
## left surface model is present.
## right cortex: 30527 surface vertices, 2 measurements.
## right surface model is present.
Alternatively, we could have provided the surfaces at the outset of reading the CIFTI file:
cii <- read_cifti(cifti_fnames["dtseries"], surfL_fname=surfL_fname, surfR_fname=surfR_fname)
cii## Brain Structures: left cortex, right cortex, left surface, right surface
## left cortex: 30424 surface vertices, 2 measurements.
## left surface model is present.
## right cortex: 30527 surface vertices, 2 measurements.
## right surface model is present.
Let’s take a look! view_xifti_surface(cii) displays the cortical data on the surface mesh using rgl. This function has several primary arguments:
color_mode specifies the nature of the data values: "sequential", "qualitative" and "diverging". If it is not provided, a default mode that makes sense for the data will be used.colors specifies the color palette to use. If it is not provided, a default palette that makes sense for the color_mode is used.idx controls which column(s) to display.widget and fname control the output type. By default, the plot is rendered in an Open GL window if the length of idx is one, and an embedded htmlwidget if the length of idx is greater than one.surfL and surfR specify the surface geometry to plot the data on. If not provided, the very inflated surfaces included in ciftiTools are used.Let’s see an example using each color_mode option. To reduce the size of this document, we will only show the static plots for now (see later in this document for an example of the interactive htmlwidget):
# Normally `cex.title` doesn't need to be set, as it defaults to a good choice.
# But when knitting static images this way, the default becomes a bit too big
# based on how knitting works.
view_xifti_surface(cii, idx=1, zlim=c(1,2), title="Sequential", cex.title=1.3)dtseries file; first column; sequential palette
cii <- read_cifti(cifti_fnames["dscalar"])
view_xifti_surface(
cii, idx=2, zlim=c(1,5), color_mode = "diverging",
title="Diverging", cex.title=1.3
)dscalar file; second column; diverging palette
dlabel <- view_xifti_surface(
read_cifti(cifti_fnames["dlabel"]),
# Interactively, a color legend that displays the label names will also be printed.
legend_ncol=5,
title="Qualitative", cex.title=1.3
)dlabel file; first label; palette from label metadata
(Note that the dtseries used in this example does not truly contain fMRI timeseries data, but we use it for illustration.)
view_xifti_volume(cii) displays the subcortical data in slices. To view interactively in a web browser, set interactive=TRUE. By default, a series of slices is displayed overlaid on the MNI template. The orientation, numbers of slices, index and value range can be adjusted.
# cifti_fnames["dscalar_ones"] is the only file with subcortical data
cii <- read_cifti(cifti_fnames["dscalar_ones"], brainstructures="subcortical")
view_xifti_volume(cii)## Values to be plotted range from 1 to 1.
Subcortical data (all ones)
# For information only, since papaya viewer cannot be opened during knitting
view_xifti_volume(cii, interactive = TRUE)The S3 method plot(cii) will display the cortical data if possible, and the subcortical data otherwise.
"xifti" objectsMedial wall vertices are typically masked out of the cortex_left and cortex_right components of data. In this case, a data matrix for the left cortex which includes empty rows for the medial wall vertices can be obtained with unmask_cortex(cii$data$cortex_left, cii$meta$cortex$medial_wall_mask$left) (and similarly for the right cortex). If the medial walls were not masked out in the input CIFTI file, the medial_wall_mask entries will be NULL.
The subcortical data is stored in vectorized form. To recover the subcortical volume, use unmask_vol(cii$data$subcort, cii$meta$subcort$mask, fill=NA) for the data and unmask_vol(cii$meta$subcort$labels, cii$meta$subcort$mask, fill=0) for the labels.
cii$meta$cifti$intent indicates the NIFTI intent, which corresponds to a unique CIFTI file type. dtseries files have an intent of 3002, dscalar files have an intent of 3006, and dlabel files have an intent of 3007. Other intents are not supported by ciftiTools.
A "surf" object can be read in using make_surf. They can be viewed with view_surf or, equivalently, their plot method. Here is the left hemisphere surface:
view_surf(surfL_fname)Left hemisphere surface
We can additionally render the vertices and edges. Below is the right hemisphere surface. (It has been resampled so the edges and vertices are visible; see the below section on resampling.)
small_surf <- resample_surf(make_surf(surfR_fname), 4000)
plot(small_surf, edge_color="black", vertex_size=3)Right hemisphere surface (resampled) with mesh
A "xifti" can contain surface geometry without the corresponding data; to make it, use as.xifti(surfL=make_surf(surfL_fname)).
"xifti" and writing itWe can make a "xifti" from data using as.xifti. For example, let’s make a "xifti" from the mean image (over time) of the dtseries file, and also include artificial subcortical data using the mask from "ones.dscalar.nii".
cii <- read_cifti(cifti_fnames["dtseries"])
cii2 <- read_cifti(cifti_fnames["dscalar_ones"], brainstructures="subcortical")
vol <- cii2$data$subcort
vol <- vol - 1 + matrix(rnorm(nrow(vol)*ncol(vol)), nrow=nrow(vol))
cii_new <- as.xifti(
cortexL = apply(cii$data$cortex_left, 1, mean),
cortexL_mwall = cii$meta$cortex$medial_wall_mask$left,
cortexR = apply(cii$data$cortex_right, 1, mean),
cortexR_mwall = cii$meta$cortex$medial_wall_mask$right,
subcortVol = vol,
subcortLabs = cii2$meta$subcort$labels,
subcortMask = cii2$meta$subcort$mask
)
is.xifti(cii_new)## [1] TRUE
cii_new## Brain Structures: left cortex, right cortex, subcortex
## left cortex: 30424 surface vertices, 1 measurements.
## right cortex: 30527 surface vertices, 1 measurements.
## subcortex: 31870 voxels, 1 measurements.
## subcortical labels:
##
## Cortex-L Cortex-R Accumbens-L Accumbens-R Amygdala-L
## 0 0 135 140 315
## Amygdala-R Brain Stem Caudate-L Caudate-R Cerebellum-L
## 332 3472 728 755 8709
## Cerebellum-R Diencephalon-L Diencephalon-R Hippocampus-L Hippocampus-R
## 9144 706 712 764 795
## Pallidum-L Pallidum-R Putamen-L Putamen-R Thalamus-L
## 297 260 1060 1010 1288
## Thalamus-R
## 1248
To visualize the cortical data of the new "xifti" object, we can add surface geometry with add_surf, or by providing surfaces with the surfL and surfR arguments to view_xifti_surface. We can also just use the inflated surfaces that come with ciftiTools by default:
view_xifti_surface(cii_new, title="dtseries Mean Image", zlim=c(0,4), cex.title=1.3)dtseries mean image
Here’s the subcortical data in sagittal view:
view_xifti_volume(cii_new, plane="sag")## Values to be plotted range from -4.14015431871559 to 3.85990957813536.
Subcortical data, sagittal view (with random noise)
We can also write out a new CIFTI file with write_cifti! Here’s how:
out_dir <- "output"
written_cii_fname <- file.path(out_dir, "my_new_cifti.dscalar.nii")
write_cifti(cii_new, written_cii_fname)## Writing left cortex.
## Writing right cortex.
## Writing subcortical data and labels.
## Creating CIFTI file from separated components.
# Verifies that if we read the file back in, the result matches.
# Some metadata is lost or added, but beside that, the data is the same.
cii_new_copy <- read_cifti(written_cii_fname, brainstructures="all")
try(testthat::expect_equal(cii_new$data, cii_new_copy$data))## Error : cii_new$data not equal to cii_new_copy$data.
## Component "cortex_left": Mean relative difference: 3.584765e-08
## Component "cortex_right": Mean relative difference: 3.548024e-08
## Component "subcort": Mean relative difference: 2.153234e-08
There is only a negligible difference between the original and the written-then-read copy due to rounding.
ciftiTools can resample CIFTI files to a lower resolution. Here, we resample the 32k dtseries file to 6k vertices. We also provide the surfaces and resample them in conjunction.
resampled_cii_fname <- "my_new_resampled.dtseries.nii"
resampled_surfL_fname <- "my_resampled_surfL.surf.gii"
resampled_surfR_fname <- "my_resampled_surfR.surf.gii"
cii_6k <- resample_cifti(
cifti_fnames["dtseries"], resampled_cii_fname,
resamp_res = 6000,
surfL_fname, surfR_fname,
resampled_surfL_fname, resampled_surfR_fname,
write_dir=out_dir
)## Separating CIFTI file.
## Time difference of 4.114372 secs
## Resampling CIFTI file.
## Time difference of 6.741215 secs
## Merging components into a CIFTI file...
## Time difference of 0.3482502 secs
The new files can be viewed together with read_cifti. Let’s make this plot interactive, since the meshes are now much lower-res! Try clicking and dragging around the plot to rotate, and scrolling to zoom in and out. We’ll also use a blue background color to highlight this interactive figure within the vignette.
view_xifti_surface(
read_cifti(cifti_fname=cii_6k["cifti"], surfL=cii_6k["surfL"], surfR=cii_6k["surfR"]),
bg="#d4edf5", title=".dtseries resampled to 6k", zlim=c(0,2), widget=TRUE
)